package com.jxl.springboot.game.advice;

import com.alibaba.fastjson.JSON;
import com.jxl.springboot.game.annotation.Role;
import com.jxl.springboot.game.service.UserService;
import com.jxl.springboot.game.utils.CookieUtils;
import com.jxl.springboot.game.web.WebResult;
import com.jxl.springboot.game.web.constant.Constant;
import org.apache.logging.log4j.util.Strings;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * AOP实现用户操作权限管理
 * @author 江雪林
 * @create 2021-03-20
 */
@Aspect
@Component
public class AuthenticateAdvice {
    private static final Logger log = LoggerFactory.getLogger(AuthenticateAdvice.class);
    @Autowired
    UserService userService;

    /**
     * 以权限注解标注的方法为切入点
     */
    @Pointcut("@annotation(com.jxl.springboot.game.annotation.Role)")
    private void poincut() {
    }

    /**
     * 使用 环绕通知执行 权限认证
     *
     * @param pjp
     * @return
     */
    @Around("poincut()")
    public Object around(ProceedingJoinPoint pjp) {
        Object object = null;
        //获取切入点的执行方法对象
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        //获取方法上的权限注解
        Role aRole = method.getAnnotation(Role.class);
        //获取权限
        String[] roles = aRole.value();
        log.info("-> 调用 {} 的 {} 方法。方法入参：{}，进行权限认证 <-",pjp.getTarget(),
                pjp.getSignature().getName(),
                Arrays.toString(pjp.getArgs()));
        //检查权限认证
        if (this.valid(roles) == false) {
            log.info("->  调用 {} 的 {} 方法，权限认证失败：unsuccessful <-", pjp.getTarget(),
                    pjp.getSignature().getName());
            //权限认证失败，操作失败
            return null;
        }
        try {
            log.info("->  调用 {} 的 {} 方法，\n权限认证成功 successful<-",pjp.getTarget(),
                    pjp.getSignature().getName());
            //程序放行
            object = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return object;
    }

    /**
     * 权限验证
     *
     * @param roles 权限
     * @return
     */
    private boolean valid(String[] roles) {
        //权限标识
        boolean flag = false;
        //获取request属性
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //获取请求
        HttpServletRequest request = attributes.getRequest();
        //获取用户权限
        String ucookie = CookieUtils.getCookieValueByName(request, Constant.U_ID);
        //判断cookie 中是否为空
        if(Strings.isNotBlank(ucookie)){
            //根据 UID+id 截取 user 的 id
            String urole = userService.getUserRole(Integer.parseInt(ucookie.replace(Constant.U_ID, "")));
            log.info("-> 用户权限：{} <-",urole);
            if (Strings.isNotBlank(urole)) {
                for (String role : roles) {
                    //判断用户权限
                    if (urole.equals(role)) {
                        flag = true;
                        break;
                    }
                }
            }
        }
        //如果没有用户权限，返回处理结果
        if (flag == false) {
            //处理结果
            HttpServletResponse response = attributes.getResponse();
            response.setContentType(Constant.CONTENT_TYPE);
            PrintWriter out = null;
            try {
                out = response.getWriter();
                WebResult result = new WebResult(Constant.UNSUCCESS, Constant.VALID_POWER_UNSUCCESS);
                out.print(JSON.toJSONString(result));
                out.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                out.close();
            }
        }
        return flag;
    }
}
